import { throttle } from 'lodash'

export default {
  install(Vue, options) {
    const LazyClass = Lazy(Vue)
    const lazy = new LazyClass(options)

    Vue.directive('lazy', {
      bind: lazy.add.bind(lazy)
    })
  }
}

function Lazy(Vue) {
  return class LazyClass {
    constructor(options) {
      this.options = options
      this.listenerQueue = []

      this.lazyLoadHandler = throttle(() => {
        let catIn = false
        this.listenerQueue.forEach((listener) => {
          if (listener.state.loading) return
          // 判断是否应该渲染
          catIn = listener.checkInView()
          // 加载图片
          catIn && listener.load()
        })
      }, 200)
    }

    add(el, bindings) {
      Vue.nextTick(() => {
        function scrollParent() {
          let parent = el.parentNode
          while (parent) {
            if (/scroll/.test(getComputedStyle(parent)['overflow'])) {
              return parent
            }
            parent = parent.parentNode
          }
          return parent
        }
        const parent = scrollParent()
        const src = bindings.value
        const listener = new ReactiveListener({
          el,
          src,
          elRenderer: this.elRenderer.bind(this),
          options: this.options
        })
        this.listenerQueue.push(listener)

        parent.addEventListener('scroll', this.lazyLoadHandler)
        // 初始化判断
        this.lazyLoadHandler()
      })
    }
    elRenderer(listener, state) {
      const { el } = listener
      let src = ''
      switch (state) {
        case 'loading':
          src = listener.options.loading
          break
        case 'error':
          src = listener.options.error || ''
          break
        default:
          src = listener.src
          break
      }
      el.setAttribute('src', src)
    }
  }
}

class ReactiveListener {
  constructor({ el, src, elRenderer, options }) {
    this.el = el
    this.src = src
    this.elRenderer = elRenderer
    this.options = options
    this.state = {
      loading: false
    }
  }
  checkInView() {
    // 图片的位置
    const { top } = this.el.getBoundingClientRect()
    return top < window.innerHeight * this.options.preLoad
  }
  /*
    渲染图片
  */
  load() {
    this.elRenderer(this, 'loading')
    loadImageAsync(
      this.src,
      () => {
        this.state.loading = true
        this.elRenderer(this, 'loaded')
      },
      () => {
        this.elRenderer(this, 'error')
      }
    )
  }
}
function loadImageAsync(src, resolve, reject) {
  const image = new Image()
  image.src = src
  image.onload = resolve
  image.onerror = reject
}

/*
  插件的作用: 
  1. 注册全局组件
  2. 扩展 Vue 原型方法 添加全局方法
  3. 注册全局指令和过滤器
*/
